`include "define.v"
module decoder
(   
    input   wire    [31:00]     instr,                          //  译码段输入的指令
    // 指令类型
    output  wire                instr_load      ,
    output  wire                instr_load_fp   ,
    output  wire                instr_misc_mem  ,
    output  wire                instr_op_imm    ,
    output  wire                instr_auipc     ,
    output  wire                instr_op_imm_32 ,
    output  wire                instr_store     ,
    output  wire                instr_store_fp  ,
    output  wire                instr_amo       ,
    output  wire                instr_op        ,
    output  wire                instr_lui       ,
    output  wire                instr_op_32     ,
    output  wire                instr_madd      ,
    output  wire                instr_msub      ,
    output  wire                instr_nmsub     ,
    output  wire                instr_nmadd     ,
    output  wire                instr_op_fp     ,
    output  wire                instr_branch    ,
    output  wire                instr_jalr      ,
    output  wire                instr_jal       ,
    output  wire                instr_system    ,

    // 指令funct字段解析                       
    output  wire    [02:00]     funct3,
    output  wire    [06:00]     funct7,
    output  wire    [04:00]     funct5,
    output  wire    [11:00]     funct12,
    // exu控制
    output  reg     [10:00]     alu_op,                         //  该指令的alu操作，最高位控制word_op，低三位控制alu运算
    output  reg     [08:00]     mdu_op,
    output  wire    [01:00]     alu_shift_mode,                 //  高位控制移位的方向，低位控制移位的方式（逻辑/算数）

    output  reg     [03:00]     disp_target,                    //  该指令将被派遣到的目的执行单元
    //  gpr读取控制
    output  reg     [04:00]     gpr_id1,
    output  reg     [04:00]     gpr_id2,
    //  gpr读取控制
    //  操作数选择
    output  reg                 operand1_sel,
    output  reg                 operand2_sel,
    //  gpr写回控制
    output  reg                 gpr_write_back_en,              
    output  reg     [04:00]     gpr_write_back_id,
    output  reg                 gpr_write_back_from_alu, 
    output  reg                 gpr_write_back_from_lsu,
    output  reg                 gpr_write_back_from_mdu,
    //  csr读取控制
    output  reg     [11:00]     csr_id,
    //  csr写回控制
    output  reg                 csr_write_back_en,
    output  reg     [11:00]     csr_write_back_id,
    //  生成立即数
    output  reg     [63:00]     imm,
    //  跳转偏移量
    output  reg     [63:00]     jalr_offset,
    output  reg                 illegal_instr_flag
);
    reg     [20:00]         instr_type;
    //  funct
    assign  funct3                      =   instr[14:12];
    assign  funct7                      =   instr[31:25];
    assign  funct5                      =   instr[31:27];
    assign  funct12                     =   instr[31:20];
    //  Shift mode ctrl
    assign  alu_shift_mode              =   {funct3[02],funct7[05]};
    //  immGen
    wire    [63:00]          imm_I      =   {{52{instr[31]}}, instr[31:20]};
	wire    [63:00]          imm_S      =   {{52{instr[31]}}, instr[31:25], instr[11:7]};
	//wire    [63:00]          imm_B      =   {{52{instr[31]}}, instr[7], instr[30:25], instr[11:8], 1'b0};
	wire    [63:00]          imm_U      =   {{32{instr[31]}}, instr[31:12], {12{1'b0}}};
    wire    [63:00]          imm_Z      =   {{59{1'b0}},instr[19:15]};

    always @( * ) begin
        case (instr[06:02])
            `OPCODE_LOAD   :   begin
                instr_type                          =   `INSTR_LOAD;
                disp_target                         =   `DISP_LSU;
                alu_op                              =   `ALU_ADD;
                mdu_op                              =   `MDU_MUL;
                imm                                 =   imm_I;
                gpr_id1                             =   instr[19:15];
                gpr_id2                             =   `ZERO_REG;
                operand1_sel                        =   `OPERAND1_GPR;
                operand2_sel                        =   `OPERAND2_IMM;
                gpr_write_back_en                   =   `ENABLE;
                gpr_write_back_id                   =   instr[11:07];
                gpr_write_back_from_alu             =   `DISABLE; 
                gpr_write_back_from_lsu             =   `ENABLE;
                gpr_write_back_from_mdu             =   `DISABLE;
                csr_id                              =   12'b0;
                csr_write_back_en                   =   `DISABLE;
                csr_write_back_id                   =   12'b0;
                jalr_offset                         =   64'b0;
                illegal_instr_flag                  =   instr[01:00] != 2'b11;
            end
            `OPCODE_MISC_MEM   :   begin
                instr_type                          =   `INSTR_MISC_MEM;
                disp_target                         =   `DISP_NOP;
                alu_op                              =   `ALU_NOP;
                mdu_op                              =   `MDU_MUL;
                imm                                 =   imm_I;            
                gpr_id1                             =   `ZERO_REG;
                gpr_id2                             =   `ZERO_REG;
                operand1_sel                        =   `OPERAND1_GPR;
                operand2_sel                        =   `OPERAND2_GPR;
                gpr_write_back_en                   =   `DISABLE;
                gpr_write_back_id                   =   `ZERO_REG;
                gpr_write_back_from_alu             =   `DISABLE; 
                gpr_write_back_from_lsu             =   `DISABLE;
                gpr_write_back_from_mdu             =   `DISABLE;
                csr_id                              =   12'b0;
                csr_write_back_en                   =   `DISABLE;
                csr_write_back_id                   =   12'b0;
                jalr_offset                         =   64'b0;
                illegal_instr_flag                  =   instr[01:00] != 2'b11;
            end
            `OPCODE_OP_IMM   :   begin
                instr_type                          =   `INSTR_OP_IMM;
                disp_target                         =   `DISP_ALU;
                alu_op[00]                          =   funct3                      ==  3'b000;     //ADDI
                alu_op[01]                          =   1'b0;                                       //OP_IMM没有SUB
                alu_op[02]                          =   funct3                      ==  3'b010;
                alu_op[03]                          =   funct3                      ==  3'b011;
                alu_op[04]                          =   funct3                      ==  3'b100;
                alu_op[05]                          =   funct3                      ==  3'b110;
                alu_op[06]                          =   funct3                      ==  3'b111;
                alu_op[07]                          =   funct3[01:00]               ==  2'b01;  
                alu_op[08]                          =   1'b0;
                alu_op[09]                          =   1'b0;
                alu_op[10]                          =   1'b0;                          
                mdu_op                              =   `MDU_MUL;
                imm                                 =   imm_I;
                gpr_id1                             =   instr[19:15];
                gpr_id2                             =   `ZERO_REG;
                operand1_sel                        =   `OPERAND1_GPR;
                operand2_sel                        =   `OPERAND2_IMM;
                gpr_write_back_en                   =   `ENABLE;
                gpr_write_back_id                   =   instr[11:07];
                gpr_write_back_from_alu             =   `ENABLE; 
                gpr_write_back_from_lsu             =   `DISABLE;
                gpr_write_back_from_mdu             =   `DISABLE;
                csr_id                              =   12'b0;
                csr_write_back_en                   =   `DISABLE;
                csr_write_back_id                   =   12'b0;
                jalr_offset                         =   64'b0;
                illegal_instr_flag                  =   instr[01:00] != 2'b11;
            end
            `OPCODE_AUIPC   :   begin
                instr_type                          =   `INSTR_AUIPC;
                disp_target                         =   `DISP_ALU;
                alu_op                              =   `ALU_ADD;
                mdu_op                              =   `MDU_MUL;
                imm                                 =   imm_U;
                gpr_id1                             =   `ZERO_REG;
                gpr_id2                             =   `ZERO_REG;
                operand1_sel                        =   `OPERAND1_PC;
                operand2_sel                        =   `OPERAND2_IMM;
                gpr_write_back_en                   =   `ENABLE;
                gpr_write_back_id                   =   instr[11:07];
                gpr_write_back_from_alu             =   `ENABLE; 
                gpr_write_back_from_lsu             =   `DISABLE;
                gpr_write_back_from_mdu             =   `DISABLE;
                csr_id                              =   12'b0;
                csr_write_back_en                   =   `DISABLE;
                csr_write_back_id                   =   12'b0;
                jalr_offset                         =   64'b0;
                illegal_instr_flag                  =   instr[01:00] != 2'b11;
            end
            `OPCODE_OP_IMM_32  :   begin
                instr_type                          =   `INSTR_OP_IMM_32;
                disp_target                         =   `DISP_ALU;
                alu_op[00]                          =   1'b0;  
                alu_op[01]                          =   1'b0;
                alu_op[02]                          =   1'b0;
                alu_op[03]                          =   1'b0;
                alu_op[04]                          =   1'b0;
                alu_op[05]                          =   1'b0;
                alu_op[06]                          =   1'b0;
                alu_op[07]                          =   1'b0;  
                alu_op[08]                          =   funct3          ==  3'b000;             // ADDIW
                alu_op[09]                          =   1'b0;
                alu_op[10]                          =   funct3[01:00]   ==  2'b01;
                //                                                                    
                mdu_op                              =   `MDU_MUL;
                imm                                 =   imm_I;
                gpr_id1                             =   instr[19:15];
                gpr_id2                             =   `ZERO_REG;
                operand1_sel                        =   `OPERAND1_GPR;
                operand2_sel                        =   `OPERAND2_IMM;
                gpr_write_back_en                   =   `ENABLE;
                gpr_write_back_id                   =   instr[11:07];
                gpr_write_back_from_alu             =   `ENABLE; 
                gpr_write_back_from_lsu             =   `DISABLE;
                gpr_write_back_from_mdu             =   `DISABLE;
                csr_id                              =   12'b0;
                csr_write_back_en                   =   `DISABLE;
                csr_write_back_id                   =   12'b0;
                jalr_offset                         =   64'b0;
                illegal_instr_flag                  =   instr[01:00] != 2'b11;
            end
            `OPCODE_STORE  :   begin
                instr_type                          =   `INSTR_STORE;
                disp_target                         =   `DISP_LSU;
                alu_op                              =   `ALU_ADD; 
                mdu_op                              =   `MDU_MUL;                        
                imm                                 =   imm_S;
                gpr_id1                             =   instr[19:15];
                gpr_id2                             =   instr[24:20];
                operand1_sel                        =   `OPERAND1_GPR;
                operand2_sel                        =   `OPERAND2_IMM;
                gpr_write_back_en                   =   `DISABLE;
                gpr_write_back_id                   =   `ZERO_REG;
                gpr_write_back_from_alu             =   `DISABLE; 
                gpr_write_back_from_lsu             =   `DISABLE;
                gpr_write_back_from_mdu             =   `DISABLE;
                csr_id                              =   12'b0;
                csr_write_back_en                   =   `DISABLE;
                csr_write_back_id                   =   12'b0;
                jalr_offset                         =   64'b0;
                illegal_instr_flag                  =   instr[01:00] != 2'b11;
            end
            `OPCODE_OP     :   begin
                instr_type                          =   `INSTR_OP;
                disp_target                         =   funct7[00]   ?   `DISP_MDU   :   `DISP_ALU;
                alu_op[00]                          =   funct3                      ==  3'b000 & !funct7[05];  
                alu_op[01]                          =   funct3                      ==  3'b000 & funct7[05];
                alu_op[02]                          =   funct3                      ==  3'b010;
                alu_op[03]                          =   funct3                      ==  3'b011;
                alu_op[04]                          =   funct3                      ==  3'b100;
                alu_op[05]                          =   funct3                      ==  3'b110;
                alu_op[06]                          =   funct3                      ==  3'b111;
                alu_op[07]                          =   funct3[01:00]               ==  2'b01;  
                alu_op[08]                          =   1'b0;
                alu_op[09]                          =   1'b0;
                alu_op[10]                          =   1'b0;
                   
                mdu_op[00]                          =   funct3                      ==  3'b000;  
                mdu_op[01]                          =   funct3                      ==  3'b001;
                mdu_op[02]                          =   funct3                      ==  3'b010;
                mdu_op[03]                          =   funct3                      ==  3'b011;
                mdu_op[04]                          =   funct3                      ==  3'b100;
                mdu_op[05]                          =   funct3                      ==  3'b101;
                mdu_op[06]                          =   funct3                      ==  3'b110;
                mdu_op[07]                          =   funct3                      ==  3'b111;
                mdu_op[08]                          =   1'b0; 
                imm                                 =   imm_I;
                gpr_id1                             =   instr[19:15];
                gpr_id2                             =   instr[24:20];
                operand1_sel                        =   `OPERAND1_GPR;
                operand2_sel                        =   `OPERAND2_GPR;
                gpr_write_back_en                   =   `ENABLE;
                gpr_write_back_id                   =   instr[11:07];
                gpr_write_back_from_alu             =   !funct7[00]; 
                gpr_write_back_from_lsu             =   `DISABLE;
                gpr_write_back_from_mdu             =   funct7[00];
                csr_id                              =   12'b0;
                csr_write_back_en                   =   `DISABLE;
                csr_write_back_id                   =   12'b0;
                jalr_offset                         =   64'b0;
                illegal_instr_flag                  =   instr[01:00] != 2'b11;
            end
            `OPCODE_LUI   :   begin
                instr_type                          =   `INSTR_LUI;
                disp_target                         =   `DISP_ALU;
                alu_op                              =   `ALU_ADD;
                mdu_op                              =   `MDU_MUL;
                imm                                 =   imm_U;
                gpr_id1                             =   `ZERO_REG;
                gpr_id2                             =   `ZERO_REG;
                operand1_sel                        =   `OPERAND1_GPR;
                operand2_sel                        =   `OPERAND2_IMM;
                gpr_write_back_en                   =   `ENABLE;
                gpr_write_back_id                   =   instr[11:07];
                gpr_write_back_from_alu             =   `ENABLE; 
                gpr_write_back_from_lsu             =   `DISABLE;
                gpr_write_back_from_mdu             =   `DISABLE;
                csr_id                              =   12'b0;
                csr_write_back_en                   =   `DISABLE;
                csr_write_back_id                   =   12'b0;
                jalr_offset                         =   64'b0;
                illegal_instr_flag                  =   instr[01:00] != 2'b11;
            end
            `OPCODE_OP_32  :   begin
                instr_type                          =   `INSTR_OP_32;
                disp_target                         =   funct7[00]   ?   `DISP_MDU   :   `DISP_ALU;
                //
                alu_op[00]                          =   1'b0;  
                alu_op[01]                          =   1'b0;
                alu_op[02]                          =   1'b0;
                alu_op[03]                          =   1'b0;
                alu_op[04]                          =   1'b0;
                alu_op[05]                          =   1'b0;
                alu_op[06]                          =   1'b0;
                alu_op[07]                          =   1'b0;
                alu_op[08]                          =   funct3          ==  3'b000 & !funct7[05];               // ADDW
                alu_op[09]                          =   funct3          ==  3'b000 & funct7[05];                // SUBW
                alu_op[10]                          =   funct3[01:00]   ==  2'b01; 
                //
                mdu_op[00]                          =   funct3          ==  3'b000;  
                mdu_op[01]                          =   funct3          ==  3'b001;
                mdu_op[02]                          =   funct3          ==  3'b010;
                mdu_op[03]                          =   funct3          ==  3'b011;
                mdu_op[04]                          =   funct3          ==  3'b100;
                mdu_op[05]                          =   funct3          ==  3'b101;
                mdu_op[06]                          =   funct3          ==  3'b110;
                mdu_op[07]                          =   funct3          ==  3'b111;
                mdu_op[08]                          =   1'b1;                           
                imm                                 =   imm_I;
                gpr_id1                             =   instr[19:15];
                gpr_id2                             =   instr[24:20];
                operand1_sel                        =   `OPERAND1_GPR;
                operand2_sel                        =   `OPERAND2_GPR;
                gpr_write_back_en                   =   `ENABLE;
                gpr_write_back_id                   =   instr[11:07];
                gpr_write_back_from_alu             =   !funct7[00]; 
                gpr_write_back_from_lsu             =   `DISABLE;
                gpr_write_back_from_mdu             =   funct7[00];
                csr_id                              =   12'b0;
                csr_write_back_en                   =   `DISABLE;
                csr_write_back_id                   =   12'b0;
                jalr_offset                         =   64'b0;
                illegal_instr_flag                  =   instr[01:00] != 2'b11;
            end
            `OPCODE_BRANCH     :   begin
                instr_type                          =   `INSTR_BRANCH;
                disp_target                         =   `DISP_NOP;
                alu_op                              =   `ALU_NOP;  
                mdu_op                              =   `MDU_MUL;                        
                imm                                 =   imm_I;
                gpr_id1                             =   instr[19:15];
                gpr_id2                             =   instr[24:20];
                operand1_sel                        =   `OPERAND1_GPR;
                operand2_sel                        =   `OPERAND2_GPR;
                gpr_write_back_en                   =   `DISABLE;
                gpr_write_back_id                   =   `ZERO_REG;
                gpr_write_back_from_alu             =   `DISABLE; 
                gpr_write_back_from_lsu             =   `DISABLE;
                gpr_write_back_from_mdu             =   `DISABLE;
                csr_id                              =   12'b0;
                csr_write_back_en                   =   `DISABLE;
                csr_write_back_id                   =   12'b0;
                jalr_offset                         =   64'b0;
                illegal_instr_flag                  =   instr[01:00] != 2'b11;
            end
            `OPCODE_JALR     :   begin
                instr_type                          =   `INSTR_JALR;
                disp_target                         =   `DISP_ALU;
                alu_op                              =   `ALU_ADD; 
                mdu_op                              =   `MDU_MUL;                         
                imm                                 =   64'd4;
                gpr_id1                             =   instr[19:15];
                gpr_id2                             =   `ZERO_REG;
                operand1_sel                        =   `OPERAND1_PC;
                operand2_sel                        =   `OPERAND2_IMM;
                gpr_write_back_en                   =   `ENABLE;
                gpr_write_back_id                   =   instr[11:07];
                gpr_write_back_from_alu             =   `ENABLE; 
                gpr_write_back_from_lsu             =   `DISABLE;
                gpr_write_back_from_mdu             =   `DISABLE;
                csr_id                              =   12'b0;
                csr_write_back_en                   =   `DISABLE;
                csr_write_back_id                   =   12'b0;
                jalr_offset                         =   imm_I;
                illegal_instr_flag                  =   instr[01:00] != 2'b11;
            end
            `OPCODE_JAL     :   begin
                instr_type                          =   `INSTR_JAL;
                disp_target                         =   `DISP_ALU;
                alu_op                              =   `ALU_ADD;
                mdu_op                              =   `MDU_MUL;                          
                imm                                 =   64'd4;
                gpr_id1                             =   `ZERO_REG;
                gpr_id2                             =   `ZERO_REG;
                operand1_sel                        =   `OPERAND1_PC;
                operand2_sel                        =   `OPERAND2_IMM;
                gpr_write_back_en                   =   `ENABLE;
                gpr_write_back_id                   =   instr[11:07];
                gpr_write_back_from_alu             =   `ENABLE; 
                gpr_write_back_from_lsu             =   `DISABLE;
                gpr_write_back_from_mdu             =   `DISABLE;
                csr_id                              =   12'b0;
                csr_write_back_en                   =   `DISABLE;
                csr_write_back_id                   =   12'b0;
                jalr_offset                         =   64'b0;
                illegal_instr_flag                  =   instr[01:00] != 2'b11;
            end
            `OPCODE_SYSTEM    :   begin
                instr_type                          =   `INSTR_SYSTEM;
                disp_target                         =   {2'b0,(|funct3),1'b0};  //  `DISP_CGU:4'b0010
                alu_op                              =   `ALU_NOP;
                mdu_op                              =   `MDU_MUL;                          
                imm                                 =   {64{funct3[02]}} & imm_Z;
                gpr_id1                             =   {5{!funct3[02]}} & instr[19:15];
                gpr_id2                             =   `ZERO_REG;
                operand1_sel                        =   `OPERAND1_GPR;
                operand2_sel                        =   funct3[02];
                gpr_write_back_en                   =   (|funct3);
                gpr_write_back_id                   =   instr[11:07];
                gpr_write_back_from_alu             =   (|funct3); 
                gpr_write_back_from_lsu             =   `DISABLE;
                gpr_write_back_from_mdu             =   `DISABLE;
                csr_id                              =   instr[31:20];
                csr_write_back_en                   =   (|funct3);
                csr_write_back_id                   =   instr[31:20];
                jalr_offset                         =   64'b0;
                illegal_instr_flag                  =   instr[01:00] != 2'b11;
            end
            default: begin
                instr_type                          =   `INSTR_ILLEGAL;
                disp_target                         =   `DISP_NOP;
                alu_op                              =   `ALU_NOP;
                mdu_op                              =   `MDU_MUL;
                imm                                 =   imm_I;
                gpr_id1                             =   `ZERO_REG;
                gpr_id2                             =   `ZERO_REG;
                operand1_sel                        =   `OPERAND1_GPR;
                operand2_sel                        =   `OPERAND2_IMM;
                gpr_write_back_en                   =   `DISABLE;
                gpr_write_back_id                   =   instr[11:07];
                gpr_write_back_from_alu             =   `DISABLE; 
                gpr_write_back_from_lsu             =   `DISABLE;
                gpr_write_back_from_mdu             =   `DISABLE;
                csr_id                              =   12'b0;
                csr_write_back_en                   =   `DISABLE;
                csr_write_back_id                   =   12'b0;
                jalr_offset                         =   64'b0;
                illegal_instr_flag                  =   1'b1;
            end
        endcase 
    end
//
    assign      instr_load          =   instr_type[00];
    assign      instr_load_fp       =   instr_type[01];
    assign      instr_misc_mem      =   instr_type[02];
    assign      instr_op_imm        =   instr_type[03];
    assign      instr_auipc         =   instr_type[04];
    assign      instr_op_imm_32     =   instr_type[05];
    assign      instr_store         =   instr_type[06];
    assign      instr_store_fp      =   instr_type[07];
    assign      instr_amo           =   instr_type[08];
    assign      instr_op            =   instr_type[09];
    assign      instr_lui           =   instr_type[10];
    assign      instr_op_32         =   instr_type[11];
    assign      instr_madd          =   instr_type[12];
    assign      instr_msub          =   instr_type[13];
    assign      instr_nmsub         =   instr_type[14];
    assign      instr_nmadd         =   instr_type[15];
    assign      instr_op_fp         =   instr_type[16];
    assign      instr_branch        =   instr_type[17];
    assign      instr_jalr          =   instr_type[18];
    assign      instr_jal           =   instr_type[19];
    assign      instr_system        =   instr_type[20];


endmodule